<template>
    <div class="flex flex-col gap-15px h-100%" :class="attrs.class" :style="attrs.style">
        <el-input v-model="modelFilterText" :placeholder="placeholder" v-if="showFilterInput">
            <template #suffix>
                <i class="i-icon-park-outline-search text-22px"></i>
            </template>
        </el-input>
        <div class="flex-[1_0] min-h-0px">
            <el-scrollbar>
                <el-tree
                    ref="treeRef"
                    :filter-node-method="filterNode"
                    default-expand-all
                    :expand-on-click-node="false"
                    :highlight-current="true"
                    v-bind="{ ...omit(attrs, ['class', 'style']), class: treeClass, style: treeStyle }"
                    :nodeKey="nodeKey"
                    :data="data"
                    :props="props.props"
                    @nodeClick="handleNodeClick"
                    @currentChange="handleCurrentChange"
                >
                    <template v-if="disabledNode" #default="scope">
                        <div class="el-custom-tree-node__label" :class="{ 'is-disabled': disabledNode(scope) }">
                            {{ scope.data[props.props.label] }}
                        </div>
                    </template>
                    <template v-else-if="$slots.default" #default="scope">
                        <slot v-bind="scope"></slot>
                    </template>
                </el-tree>
            </el-scrollbar>
        </div>
    </div>
</template>

<script setup>
import { computed, nextTick, ref, unref, useAttrs, watch } from 'vue'
import { omit } from 'lodash-es'

defineOptions({
    name: 'BaseFilterTree',
    inheritAttrs: false
})

const props = defineProps({
    treeClass: {
        type: [String, Array, Object]
    },
    treeStyle: {
        type: [String, Array, Object]
    },
    showFilterInput: {
        type: Boolean,
        default: true
    },
    modelValue: {
        type: [String, Number]
    },
    filterText: {
        type: [String, Number]
    },
    placeholder: {
        type: [String, Number],
        default: ''
    },
    props: {
        type: Object,
        default: () => ({
            children: 'children',
            label: 'label'
        })
    },
    nodeKey: {
        type: String
    },
    data: {
        type: Array,
        default: () => []
    },
    onNodeClick: {
        type: Function
    },
    onCurrentChange: {
        type: Function
    },
    clickFirstOnDataChange: {
        type: Boolean,
        default: false
    },
    disabledNode: {
        type: Function
    },
    // 是否允许所有nodeClick事件
    allowAllNodeClickEvent: {
        type: Boolean,
        default: false
    }
})

const attrs = useAttrs()

const emit = defineEmits(['update:modelValue', 'update:filterText'])

const treeRef = ref()

const _filterText = ref('')

const filterNode = (value, data) => {
    if (!value) return true
    return data[props.props.label].includes(value)
}

const modelFilterText = computed({
    get () {
        if (props.filterText !== undefined) return props.filterText
        return _filterText.value
    },
    set (val) {
        _filterText.value = val
        emit('update:filterText', val)
    }
})

watch(modelFilterText, val => {
    treeRef.value.filter(val)
})

watch(
    () => props.modelValue,
    async () => {
        await nextTick()
        const treeInstance = treeRef.value
        if (!treeInstance) return
        const node = treeInstance.getNode(props.modelValue)
        treeInstance.setCurrentKey(node ? node.key : null)
    }
)

watch(() => [props.data.length, props.data], async () => {
    if (!props.clickFirstOnDataChange) return
    if (!props.data.length) return
    if (!props.nodeKey) return
    await nextTick()
    props.onNodeClick?.(props.data[0])
    treeRef.value?.setCurrentKey(props.data[0][props.nodeKey])
}, {
    immediate: true
})

const handleNodeClick = (...args) => {
    const [data, node, treeNode, event] = args
    const ctx = { data, node }
    if (!props.allowAllNodeClickEvent && props.disabledNode) {
        const isDisabledNode = props.disabledNode(ctx)
        if (isDisabledNode) return
    }
    props.onNodeClick?.(...args)
}

const currentNode = ref(null)
const currentData = ref(null)

const handleCurrentChange = (...args) => {
    const [data, node] = args
    const ctx = { data, node }
    if (props.disabledNode) {
        if (data?.[props.nodeKey] === currentData.value?.[props.nodeKey]) return
        const isDisabledNode = props.disabledNode(ctx)
        if (isDisabledNode) {
            if (currentData.value) {
                treeRef.value.setCurrentKey(currentData.value[props.nodeKey])
            } else {
                treeRef.value.setCurrentKey(null)
            }
            return
        }
    }
    currentNode.value = node
    currentData.value = data
    props.onCurrentChange?.(ctx)
    emit('update:modelValue', node ? node.key : null)
}

defineExpose({
    treeRef,
    filterText: modelFilterText,
    currentNode,
    currentData
})
</script>

<style lang="scss" scoped>
.el-tree {
    background-color: transparent;
}

.el-custom-tree-node__label {
    flex: 1 0;

    &.is-disabled {
        color: var(--el-text-color-placeholder);
        cursor: not-allowed;
    }
}
</style>